package com.sjzd.net;

import com.sjzd.exception.NullObjectException;
import com.sjzd.menu.GameMenu;
import com.sjzd.message.*;

import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SocketChannel;

public class ClientNet {
    private SocketChannel sc = null;
    private DatagramChannel dc = null;
    private ByteBuffer buf = null;
    private String id = null;
    private GameMenu gameMenu = null;
    private TankMessageProcessStrategy tankMessageProcessStrategy = null;
    private BulletMessageProcessStrategy bulletMessageProcessStrategy = null;
    private InetSocketAddress serverUDPAddress = null;
    private ConnectConfigParser parser = null;
    public ClientNet(String nickname) {
        this.gameMenu = new GameMenu(nickname, this);
        tankMessageProcessStrategy = new TankMessageProcessStrategy(gameMenu);
        bulletMessageProcessStrategy = new BulletMessageProcessStrategy(gameMenu);
        parser = new ConnectConfigParser();
        serverUDPAddress = new InetSocketAddress(parser.getServerIP(), parser.getServerUDPPort());
    }

    public GameMenu getGameMenu() {
        return gameMenu;
    }

    public void connect() {
        buf = ByteBuffer.allocate(1024);
        try {
            sc = SocketChannel.open(new InetSocketAddress(parser.getServerIP(), parser.getServerTCPPort()));
            sc.configureBlocking(false);
            // 获取服务器发来的id分配信息，并创建玩家坦克
            if ((id = getIdFromServer()) == null) {
                System.out.println("与服务器建立连接失败!");
                System.exit(0);
            }
            System.out.println("与服务器建立连接成功!");
            // 向服务器发送自己的UDP端口号
            buf.clear();
            String msg = Protocol.CONNECT_SYMBOL.getSymbol() + parser.getClientUDPPort() +
                    Protocol.CONNECT_SYMBOL.getSymbol();
            buf.put(msg.getBytes());
            sc.write(buf);
            System.out.println("已发送自己的UDP端口号给服务器");
            // 启动UDP连接
            dc = DatagramChannel.open();
            dc.bind(new InetSocketAddress(parser.getClientUDPPort()));
            dc.configureBlocking(false);
        } catch (Exception e) {
            System.err.println("与服务器建立连接过程中出现错误！");
            e.printStackTrace();
        }
    }

    private String getIdFromServer() {
        buf.clear();
        String msg = "";
        int len = 0;
        try {
            while (true) {
                if ((len = sc.read(buf)) > 0) {
                    buf.flip();
                    msg = new String(buf.array(), 0, len);
                    msg = msg.substring(Protocol.CONNECT_SYMBOL.getSymbol().length(),
                            len - Protocol.CONNECT_SYMBOL.getSymbol().length());
                    return msg;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void udpMonitor() {
        int len = 0;
        while (true) {
            buf.clear();
            try {
                if (null == dc.receive(buf)) {
                    return;
                }
                processReceiveMsg();
            } catch (IOException e) {
                System.err.println("接受数据包的过程中出现致命错误，正在尝试重连！");
            }
        }
    }

    public void turnOff() {
        if (null != dc) {
            try {
                dc.close();
            } catch (IOException e) {
                System.out.println("客户端关闭时出现异常！" + e.getMessage());
            }
        }
    }

    /**
     *  发送消息
     * @param statusMessageBase
     */
    public void sendMsg(StatusMessageBase statusMessageBase) {
        byte[] bytes = new byte[1024];
        ByteArrayOutputStream baos = null;
        ObjectOutputStream oos = null;
        try {
            baos = new ByteArrayOutputStream(1024);
            oos = new ObjectOutputStream(baos);
            oos.writeObject(statusMessageBase);
            buf.clear();
            buf.put(baos.toByteArray());
            dc.send(buf, serverUDPAddress);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                baos.close();
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *  处理收到的消息
     */
    private void processReceiveMsg() {
        ByteArrayInputStream bais = null;
        ObjectInputStream ois = null;
        try {
            bais = new ByteArrayInputStream(buf.array());
            ois = new ObjectInputStream(bais);
            StatusMessageBase msg = (StatusMessageBase)ois.readObject();
            parseMsg(msg);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                bais.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *  策略模式解析数据
     * @param msg
     */
    private void parseMsg(StatusMessageBase msg) throws NullObjectException {
        if (msg instanceof TankStatusMessage) {
            tankMessageProcessStrategy.process((TankStatusMessage) msg);
        } else if (msg instanceof BulletStatusMessage) {
            bulletMessageProcessStrategy.process((BulletStatusMessage) msg);
        }
    }
}
